package com.hgyc.mom.core.aspectj;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hgyc.mom.common.vo.Result;
import com.hgyc.mom.core.annotation.DictColumn;
import com.hgyc.mom.core.annotation.TableDict;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ClassUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * controller返回值拦截器，翻译Relation注解标注的字段
 * @author fwj
 * @since 2024/9/28
 */
@Aspect
@Component
@Slf4j
public class TableDictAspect {

    @Autowired
    public RedisTemplate redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 定义切点Pointcut
     */
    @Pointcut("execution(public * com.hgyc.mom..*.*Controller.*(..))")
    public void executeService() {
    }

    @Around("executeService()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        result = this.parseDictText(result);
        log.debug("注入字典到JSON数据耗时 {} ms", (System.currentTimeMillis() - start));
        return result;
    }

    private Object parseDictText(Object result) {
        //这里只翻译分页和List的返回值
        if (result instanceof Result) {
            Result ajaxResult = (Result) result;

            List<Object> dataList = null;
            if (Objects.nonNull(ajaxResult.getData())) {
                if (ajaxResult.getData() instanceof Collection<?>) {
                    dataList = (List<Object>) ajaxResult.getData();
                } else if (ajaxResult.getData() instanceof IPage) {
                    dataList = ((Page) ajaxResult.getData()).getRecords();
                }
            }

            if (!CollectionUtils.isEmpty(dataList)) {
                //先判断是否有转化字段
                if (!this.checkHasDict(dataList.get(0))) {
                    return ajaxResult;
                }

                ArrayNode arrayNode = objectMapper.createArrayNode();
                for (Object item : dataList) {
                    JsonNode resultNode = processObjectDictFields(item);
                    arrayNode.add(resultNode);
                }

                List<Object> objectList = objectMapper.convertValue(arrayNode, new TypeReference<>() {
                });
                if (ajaxResult.getData() instanceof IPage) {
                    ((IPage) ajaxResult.getData()).setRecords(objectList);
                } else {
                    ajaxResult.setData(objectList);
                }
            }

            return ajaxResult;
        } else {
            return result;
        }
    }

    /**
     * 检测是否有字典注解
     * @param obj
     * @return
     */
    private boolean checkHasDict(Object obj) {
        boolean flag = false;

        //判断是否基础类型或包装类型
        if(ClassUtils.isPrimitiveOrWrapper(obj.getClass())){
            return false;
        }

        List<Field> allFields = getAllFields(obj.getClass());

        for (Field field : allFields) {
            TableDict tableDict = field.getAnnotation(TableDict.class);
            if (tableDict != null) {
                flag = true;
                return flag;
            }
        }

        return flag;
    }

    /**
     * 处理对象中的@TableDict注解字段
     */
    private JsonNode processObjectDictFields(Object obj) {
        // 获取对象所有字段（包括父类）
        List<Field> allFields = getAllFields(obj.getClass());

        // 按 table + columns 签名分组，避免混乱
        Map<String, List<DictQueryInfo>> queryMap = new HashMap<>();

        // 第一遍扫描：收集需要翻译的字段信息
        for (Field field : allFields) {
            TableDict tableDict = field.getAnnotation(TableDict.class);
            if (tableDict != null) {
                try {
                    field.setAccessible(true);
                    Object fieldValue = field.get(obj);
                    if (fieldValue != null) {
                        String groupKey = buildGroupKey(tableDict.table(), tableDict.columns());
                        String cacheKey = buildCacheKey(tableDict.table(), fieldValue.toString(), groupKey);

                        queryMap.computeIfAbsent(groupKey, k -> new ArrayList<>())
                                .add(new DictQueryInfo(cacheKey, tableDict, fieldValue, field));
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("反射访问字段失败: " + field.getName(), e);
                }
            }
        }


        // 将对象转为JSON树
        ObjectNode jsonNode = objectMapper.valueToTree(obj);

        // 批量处理每个表的翻译需求
        queryMap.forEach((groupKey, queryInfos) -> {
            DictQueryInfo first = queryInfos.get(0);
            String tableName = first.tableDict().table();
            String keyColumn = first.tableDict().key();
            DictColumn[] columns = first.tableDict().columns();

            List<String> cacheKeys = queryInfos.stream().map(DictQueryInfo::cacheKey).toList();
            Map<String, Map<String, String>> cachedData = batchGetFromRedis(cacheKeys);

            List<DictQueryInfo> needDbQuery = queryInfos.stream()
                    .filter(info -> !cachedData.containsKey(info.cacheKey()))
                    .toList();

            if (!needDbQuery.isEmpty()) {
                Map<String, Map<String, String>> dbData = batchQueryFromDatabase(tableName, keyColumn, columns, needDbQuery, groupKey);
                cachedData.putAll(dbData);
            }

            queryInfos.forEach(info -> {
                Map<String, String> translated = cachedData.get(info.cacheKey());
                if (translated != null) {
                    translated.forEach(jsonNode::put);
                }
            });
        });

        return jsonNode;
    }

    // 生成分组Key，区分不同columns配置
    private String buildGroupKey(String table, DictColumn[] columns) {
        String columnsKey = Arrays.stream(columns)
                .map(c -> c.name() + ":" + c.alias())
                .sorted()
                .collect(Collectors.joining(","));
        return table + "::" + columnsKey;
    }

    // 构建缓存Key，加入 groupKey 保证唯一
    private String buildCacheKey(String table, String keyValue, String groupKey) {
        return table + "::" + keyValue + "::" + groupKey;
    }

    /**
     * 从Redis批量获取缓存数据
     */
    private Map<String, Map<String, String>> batchGetFromRedis(List<String> cacheKeys) {
        // 1. 批量获取Redis值
        List<Object> cachedValues = redisTemplate.opsForValue().multiGet(cacheKeys);
        if (cachedValues == null) {
            return Collections.emptyMap();
        }

        // 2. 构建结果Map
        Map<String, Map<String, String>> result = new HashMap<>();
        for (int i = 0; i < cacheKeys.size();  i++) {
            Object value = cachedValues.get(i);
            if (value != null) {
                try {
                    // 3. 类型安全转换
                    Map<String, String> translatedMap = objectMapper.convertValue(value,
                            new TypeReference<>() {
                            });
                    result.put(cacheKeys.get(i),  translatedMap);
                } catch (IllegalArgumentException e) {
                    log.warn("Redis 缓存值转换失败 key={}", cacheKeys.get(i),  e);
                }
            }
        }
        return result;
    }

    /**
     * 批量查询数据库并更新缓存
     */
    private Map<String, Map<String, String>> batchQueryFromDatabase(
            String tableName,
            String keyColumn,
            DictColumn[] columns,
            List<DictQueryInfo> queryInfos,
            String groupKey
    ) {
        List<Object> keyValues = queryInfos.stream().map(DictQueryInfo::fieldValue).toList();

        String selectColumns = Arrays.stream(columns)
                .map(DictColumn::name)
                .collect(Collectors.joining(", "));

        String placeholders = keyValues.stream().map(v -> "?").collect(Collectors.joining(", "));

        String sql = String.format("SELECT %s, %s FROM %s WHERE %s IN (%s)",
                keyColumn, selectColumns, tableName, keyColumn, placeholders);

        List<Map<String, Object>> dbResults = jdbcTemplate.queryForList(sql, keyValues.toArray());

        Map<String, Map<String, String>> result = new HashMap<>();

        for (Map<String, Object> row : dbResults) {
            String keyValue = row.get(keyColumn).toString();
            String cacheKey = buildCacheKey(tableName, keyValue, groupKey);

            Map<String, String> translated = new HashMap<>();
            for (DictColumn column : columns) {
                translated.put(column.alias(), row.get(column.name()).toString());
            }

            redisTemplate.opsForValue().set(
                    cacheKey, translated, 12 + new Random().nextInt(12), TimeUnit.HOURS
            );

            result.put(cacheKey, translated);
        }

        return result;
    }

    private List<Field> getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        while (clazz != null) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }
        return fields;
    }

    /**
     * 字典查询信息封装类
     * @param cacheKey Redis缓存键（格式：dict:{table}:{keyValue}）
     * @param tableDict 表字典注解实例
     * @param fieldValue 字段原始值（需翻译的键值）
     * @param field 被注解标记的反射字段对象
     */
    private record DictQueryInfo(
            String cacheKey,
            TableDict tableDict,
            Object fieldValue,
            Field field
    ) {
        /**
         * 获取字段名称（被@TableDict标注的字段名）
         */
        public String getFieldName() {
            return field.getName();
        }

        /**
         * 获取表名（从@TableDict注解提取）
         */
        public String getTableName() {
            return tableDict.table();
        }

        /**
         * 构建查询列名列表（逗号分隔）
         */
        public String getQueryColumns() {
            return Arrays.stream(tableDict.columns())
                    .map(DictColumn::name)
                    .collect(Collectors.joining(","));
        }

        /**
         * 转换为Redis批量查询的标准化键（用于multiGet）
         */
        public static List<String> toCacheKeys(List<DictQueryInfo> queries) {
            return queries.stream()
                    .map(DictQueryInfo::cacheKey)
                    .collect(Collectors.toList());
        }

        /**
         * 将查询结果绑定到原始对象
         * @param target 目标对象（需要添加翻译字段的实例）
         * @param translated 翻译结果（key: 别名, value: 翻译值）
         */
        public void bindToTarget(Object target, Map<String, String> translated) {
            try {
                field.setAccessible(true);
                for (DictColumn column : tableDict.columns())  {
                    String translatedValue = translated.get(column.alias());
                    if (translatedValue != null) {
                        // 动态添加字段到目标对象
                        addDynamicField(target, column.alias(),  translatedValue);
                    }
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(
                        String.format(" 字段绑定失败: %s.%s",
                                target.getClass().getSimpleName(),
                                field.getName()),
                        e
                );
            }
        }

        private void addDynamicField(Object target, String fieldName, Object value)
                throws IllegalAccessException {
            // 方案1：反射动态添加（需目标对象支持）
            try {
                Field dynamicField = target.getClass().getDeclaredField(fieldName);
                dynamicField.setAccessible(true);
                dynamicField.set(target,  value);
            }
            // 方案2：如果是Map类型
            catch (NoSuchFieldException e) {
                if (target instanceof Map) {
                    ((Map<String, Object>) target).put(fieldName, value);
                }
            }
        }
    }
}
